/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.regex.Pattern;

import ore.errors.OrekitException;
import ore.frames.EOP;
import ore.time.DateComponents;

/**
 * Loader for EOP 05 C04 files.
 * 
 * <p>EOP 05 C04 files contain {@link EOP Earth
 * Orientation Parameters} consistent with ITRF2005 for one year
 * periods.</p>
 * 
 * <p>The EOP 05 C04 files are recognized thanks to their base names,
 * which must match one of the the patterns
 * <code>eopc04_IAU2000.##</code> or <code>eopc04.##</code> (or the
 * same ending with <code>.gz</code> for gzip-compressed files) where
 * <code>'#'</code> stands for a digit character.</p>
 * 
 * <p>Between 2002 and 2007, another series of Earth Orientation
 * Parameters was in use: EOPC04 (without the 05). These parameters
 * were consistent with the previous ITRS realization: ITRF2000. These
 * files are no longer provided by IERS and only 6 files covering the
 * range 2002 to 2007 were generated. The content of these files is
 * not the same as the content of the new files supported by this
 * class, however IERS uses the same file naming convention for
 * both. If a file from the older series is found by this class, a
 * parse error will be triggered. Users must remove such files to
 * avoid being lured in believing they do have EOP data.</p> <p>Files
 * containing old data (back to 1962) have been regenerated in the new
 * file format and are available at IERS web site: <a
 * href="http://hpiers.obspm.fr/iers/eop/eopc04_05/">Index of
 * /iers/eop/eopc04_05</a>.</p>
 * 
 * @author Luc Maisonobe
 */
public class EOP05C04FilesLoader
    extends Object
    implements DataLoader
{
    private static final double ARC_SECONDS_TO_RADIANS = 2 * Math.PI / 1296000;

    private static final int SRC_YEAR_FIELD    = 0;
    private static final int SRC_MONTH_FIELD   = 1;
    private static final int SRC_DAY_FIELD     = 2;
    private static final int SRC_MJD_FIELD     = 3;
    private static final int SRC_POLE_X_FIELD  = 4;
    private static final int SRC_POLE_Y_FIELD  = 5;
    private static final int SRC_UT1_UTC_FIELD = 6;
    private static final int SRC_LOD_FIELD     = 7;
    private static final int SRC_DDEPS_FIELD   = 8;
    private static final int SRC_DDPSI_FIELD   = 9;


    /** Pattern for data lines.
     * <p>
     * The data lines in the EOP 05 C04 yearly data files have the following fixed form:
     * <p>
     * <pre>
     * year month day MJD ...12 floating values fields in decimal format...
     * 2000   1   1  51544   0.043157   0.377872   0.3555456   ...
     * 2000   1   2  51545   0.043475   0.377738   0.3547352   ...
     * 2000   1   3  51546   0.043627   0.377507   0.3538988   ...
     * </pre>
     * <p>the corresponding fortran format is:
     *  3(I4),I7,2(F11.6),2(F12.7),2(F12.6),2(F11.6),2(F12.7),2F12.6</p>
     */
    private static final Pattern LINE_PATTERN =
        Pattern.compile("^\\d+ +\\d+ +\\d+ +\\d+(?: +-?\\d+\\.\\d+){12}$");


    private final String supportedNames;

    private final Set<EOP> eop;

    /** Build a loader for IERS EOP 05 C04 files.
     * @param supportedNames regular expression for supported files names
     * @param eop set where to <em>add</em> EOP data
     * (pre-existing data is preserved)
     */
    public EOP05C04FilesLoader(final String supportedNames,
                               final Set<EOP> eop)
    {
        super();
        this.supportedNames = supportedNames;
        this.eop = eop;
    }

    /** Load Earth Orientation Parameters.
     * <p>The data is concatenated from all bulletin B data files
     * which can be found in the configured IERS directory.</p>
     * @return true if some data has been loaded
     * @exception OrekitException if some data can't be read or some
     * file content is corrupted
     */
    public boolean loadEOP() throws OrekitException {
        return DataProvidersManager.getInstance().feed(supportedNames, this);
    }

    /** {@inheritDoc} */
    public boolean stillAcceptsData() {
        return true;
    }

    /** {@inheritDoc} */
    public void loadData(final InputStream input, final String name)
        throws IOException, OrekitException {

        // set up a reader for line-oriented bulletin B files
        final BufferedReader reader = new BufferedReader(new InputStreamReader(input));

        // read all file, ignoring header
        int lineNumber = 0;
        boolean inHeader = true;
        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
            ++lineNumber;
            boolean parsed = false;

            if (LINE_PATTERN.matcher(line).matches()) {
                inHeader = false;
                // this is a data line, build an entry from the extracted fields
                final String[] fields = line.split(" +");
                final int    year  = Integer.parseInt(fields[SRC_YEAR_FIELD]);
                final int    month = Integer.parseInt(fields[SRC_MONTH_FIELD]);
                final int    day   = Integer.parseInt(fields[SRC_DAY_FIELD]);
                final int    mjd   = Integer.parseInt(fields[SRC_MJD_FIELD]);
                if (new DateComponents(year, month, day).getMJD() == mjd) {
                    // the first six fields are consistent with the expected format
                    final double x    = Double.parseDouble(fields[SRC_POLE_X_FIELD]) * ARC_SECONDS_TO_RADIANS;
                    final double y    = Double.parseDouble(fields[SRC_POLE_Y_FIELD]) * ARC_SECONDS_TO_RADIANS;
                    final double dtu1 = Double.parseDouble(fields[SRC_UT1_UTC_FIELD]);
                    final double lod  = Double.parseDouble(fields[SRC_LOD_FIELD]);
                    final double dpsi = Double.parseDouble(fields[SRC_DDPSI_FIELD]) * ARC_SECONDS_TO_RADIANS;
                    final double deps = Double.parseDouble(fields[SRC_DDEPS_FIELD]) * ARC_SECONDS_TO_RADIANS;

                    eop.add(new EOP(mjd, x, y, dtu1, lod, deps, dpsi));

                    parsed = true;
                }
            }
            if (!(inHeader || parsed)) {
                throw new OrekitException("unable to parse line {0} in IERS data file {1}",
                                          lineNumber, name);
            }
        }

        // check if we have read something
        if (inHeader) {
            throw new OrekitException("file {0} is not a supported IERS data file", name);
        }

    }

}
